/**
 * 
 */
package appStarshipPushups.server;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

import appStarshipPushups.common.Protocole;
import appStarshipPushups.common.Starship;

/**
 * Cette classe représent le serveur et son fil d'exécution.
 * @author Adam Labranche & Kevin Arsenault
 */
public class StarshipPushupsServer extends Thread
{
	// Constantes de messages d'erreur.
	private static final String LISTENING_ERROR = "Il y a eu un erreur d'écoute du client(StarshipPushupsServer)";
	private static final String CLOSING_ERROR = "Erreur de fermeture du server";
	private static final String OPENING_ERROR = "Il y a eu un erreur dans l'ouverture du server : ";
	private static final String CLIENT_CLOSING_ERROR = "Il y a eu un erreur dans la fermeture des clients au server : ";
	private static final String SOCKET_OBTAINED = "Socket obtenu";
	private static final String SOCKET_CLOSED = "Socket fermé";
	private static final String FILE_CREATION_ERROR = "Il y a eu un erreur lors de la création du fichier de sortie";
	private static final String FILE_CLOSING_ERROR = "Il y a eu un erreur lors de la fermeture du fichier.";
	
	// chemin du fichier log.
	private static final String OUTPUT_FILE = "log.txt";

	/**
	 * Variables servant à l'écriture du fichier log.
	 */
	FileOutputStream fileStream = null;
	PrintWriter fileOut = null;
	
	/**
	 * Ce membre représente le port que le serveur écoute.
	 */
	private int port = Protocole.LISTENING_PORT;
	/**
	 * Ce membre représente le "socket" du serveur.
	 */
	private ServerSocket server = null;
	/**
	 * Ce membre représente l'état du serveur, s'il est en marche.
	 */
	private boolean isRunning = false;
	/**
	 * Ce membre représente la liste des clients connectés.
	 */
	private ArrayList<PlayerThread> clientLst = new ArrayList<PlayerThread>();
	
	/**
	 * Ce membre statique représente l'instance du serveur.
	 */
	private static StarshipPushupsServer myServer = null;
	
	/**
	 * C'est le constructeur par défaut.
	 */
	private StarshipPushupsServer()
	{
		// Créer le fichier pour sortir l'information reçu.
		try
		{
			this.fileStream = new FileOutputStream(StarshipPushupsServer.OUTPUT_FILE);
			this.fileOut = new PrintWriter(this.fileStream);
		}
		catch(FileNotFoundException e)
		{
			System.err.println(StarshipPushupsServer.FILE_CREATION_ERROR);
			e.printStackTrace();
		}
	}
	
	public synchronized void log(String line)
	{
		this.fileOut.println(line);
	}
	
	public static StarshipPushupsServer getInstance()
	{
		if(StarshipPushupsServer.myServer == null)
		{
			StarshipPushupsServer.myServer = new StarshipPushupsServer();
		}
		return StarshipPushupsServer.myServer;
	}

	/**
	 * Cette fonction s'occupe d'attendre les messages venant des clients.
	 */
	private void listen()
	{
		while(this.isRunning)
		{
			try
			{
				Socket clientSocket = this.server.accept();
				PlayerThread newPlayer = new PlayerThread(clientSocket);
				this.clientLst.add(newPlayer);
				newPlayer.startConnection();
			}
			catch(IOException e)
			{
				if(!this.server.isClosed())
				{
					System.err.println(StarshipPushupsServer.LISTENING_ERROR);
					System.err.println(e.getMessage());
					try
					{
						this.server.close();
					}
					catch(IOException e2)
					{
						System.err.println(StarshipPushupsServer.CLOSING_ERROR);
						System.err.println(e2.getMessage());
					}
				}
			}
		}
	}
	
	/**
	 * Cette fonction envoye un message à tous les clients.
	 * @param msg Le message à envoyer.
	 */
	public synchronized void broadcast(String msg)
	{
		if(this.isRunning)
		{
			for(int i = 0; i < this.clientLst.size(); i++)
			{
				this.clientLst.get(i).sendMsg(msg);
			}
		}
	}
	
	/**
	 * Cette fonction envoye à tout le monde le vaisseau à effacer.
	 * @param shipID Le vaisseau à effacer.
	 */
	public synchronized void broadcastErasePlayer(int shipID)
	{
		if(this.isRunning)
		{
			for(int i = 0; i< this.clientLst.size(); i++)
			{
				this.clientLst.get(i).sendErasePlayer(shipID);
			}
		}
	}

	/**
	 * Cette fonction permet d'enlever un joueur de la partie slon le vaisseau
	 * passé en paramètre.
	 * @param aShip Le vaisseau du joueur à supprimer.
	 */
	public synchronized void deletePlayer(Starship aShip)
	{
		if(this.isRunning)
		{
			for(int i = 0; i < this.clientLst.size(); i++)
			{
				if(this.clientLst.get(i).getStarship() == aShip)
				{
					this.clientLst.get(i).quitGame();
				}
			}
		}
	}
	
	/**
	 * Éxécute le fil d'exécution.
	 */
	public void run()
	{
		this.startServer();
	}
	
	/**
	 * Cette fonction permet de démarrer le serveur.
	 */
	public void startServer()
	{
		this.isRunning = true;
		try
		{
			this.server = new ServerSocket(this.port);
			System.out.println(StarshipPushupsServer.SOCKET_OBTAINED);
		}
		catch(IOException e)
		{
			System.err.println(StarshipPushupsServer.OPENING_ERROR);
			System.err.println(e.getMessage());
		}
		this.listen();
	}
	
	public void removePlayerThread(PlayerThread thread)
	{
		if(this.clientLst.indexOf(thread) != -1)
		{
			this.clientLst.remove(thread);
		}
	}
	
	/**
	 * Cette fonction permet de modifie le port d'écoute du serveur.
	 * @param newPort Le nouveau pour d'écoute.
	 */
	public void setPort(int newPort)
	{
		this.port = newPort;
	}
	
	/**
	 * Cette fonction permet d'arrêter le serveur.
	 */
	public void stopServer()
	{
		if(StarshipPushupsServer.myServer != null)
		{
			this.isRunning = false;
			StarshipPushupsServer.myServer = null;
			this.finalize();
		}
	}
	
	/**
	 * Cette fonction permet d'arrêter le serveur.
	 */
	protected void finalize()
	{
		// Fermer le fichier.
		try
		{
			this.fileOut.close();
			this.fileStream.close();
		}
		catch(IOException e)
		{
			System.err.println(StarshipPushupsServer.FILE_CLOSING_ERROR);
			e.printStackTrace();
		}

		// On dit à chaque joueur de quitter.
		for(int i = 0; i < this.clientLst.size(); i++)
		{
			this.clientLst.get(i).quitGame();
			this.clientLst.get(i).end();
		}
		// On s'assure que la liste est vide.
		this.clientLst.clear();
		
		// On ferme le socket du serveur s'il n'est pas déjà fermé.
		try
		{
			if(this.server != null)
			{
				if(!this.server.isClosed())
				{
					this.server.close();
					System.out.println(StarshipPushupsServer.SOCKET_CLOSED);
				}
			}
		}
		catch(IOException e)
		{
			System.err.println(StarshipPushupsServer.CLIENT_CLOSING_ERROR);
			System.err.println(e.getMessage());
		}
	}
	
	
}
